/*
 * Copyright (c) 2018, apexes.net. All rights reserved.
 *
 *         http://www.apexes.net
 *
 */
package net.apexes.commons.ormlite;

import com.j256.ormlite.field.DataPersister;
import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseFieldConfig;
import net.apexes.commons.lang.Dates;
import net.apexes.commons.lang.Enume;

/**
 * 
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 *
 * @param <T>
 */
public class ColumnBuilder<T> {

    private final Table<T> table;
    private final ColumnDatabaseFieldConfig config;
    private final ColumnFactory factory;

    ColumnBuilder(Table<T> table, String fieldName, ColumnFactory factory) {
        if (fieldName == null) {
            throw new IllegalArgumentException("The fieldName must be not null.");
        }
        this.table = table;
        this.factory = factory;
        config = new ColumnDatabaseFieldConfig(fieldName);
    }

    public final Column build() {
        if (config.getColumnName() == null) {
            config.setColumnName(table.convertToDatabaseName(config.getFieldName()));
        }
        if (factory != null) {
            return factory.create(table, config);
        }
        return new ColumnImpl<>(table, config);
    }

    public final ColumnBuilder<T> columnName(String columnName) {
        config.setColumnName(columnName);
        return this;
    }

    public final ColumnBuilder<T> columnDefinition(String columnDefinition) {
        config.setColumnDefinition(columnDefinition);
        return this;
    }

    public final ColumnBuilder<T> id() {
        config.setId(true);
        return this;
    }

    public final ColumnBuilder<T> unique() {
        config.setUnique(true);
        return this;
    }

    public final ColumnBuilder<T> dataPersister(DataPersister dataPersister) {
        config.setDataPersister(dataPersister);
        return this;
    }

    public final ColumnBuilder<T> dataType(DataType dataType) {
        config.setDataType(dataType);
        return this;
    }

    public final ColumnBuilder<T> notNull() {
        config.setCanBeNull(false);
        return this;
    }

    public final ColumnBuilder<T> decimal(int width, int digits) {
        if (digits == 0) {
            config.setColumnDefinition("NUMERIC(" + width + ")");
        } else {
            config.setColumnDefinition("NUMERIC(" + width + ", " + digits + ")");
        }
        config.setWidth(width);
        config.setDigits(digits);
        config.setFormat(digitsFormat(digits));
        config.setDataType(DataType.BIG_DECIMAL);
        return this;
    }

    public final ColumnBuilder<T> numeric(int width, int digits) {
        config.setWidth(width);
        config.setDigits(digits);
        config.setFormat(digitsFormat(digits));
        if (digits == 0) {
            config.setDataType(DataType.LONG_OBJ);
            config.setDataPersister(BigDecimalLongType.getSingleton());
        } else {
            config.setDataType(DataType.DOUBLE_OBJ);
            config.setDataPersister(BigDecimalDoubleType.getSingleton());
        }
        return this;
    }

    private String digitsFormat(int digits) {
        if (digits > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("#.");
            for (int i = 0; i < digits; i++) {
                sb.append("#");
            }
            return sb.toString();
        } else {
            return "#";
        }
    }

    public final ColumnBuilder<T> real() {
        config.setDataType(DataType.DOUBLE_OBJ);
        return this;
    }

    public final ColumnBuilder<T> integer() {
        config.setDataType(DataType.INTEGER_OBJ);
        return this;
    }

    public final ColumnBuilder<T> bigint() {
        config.setDataType(DataType.LONG_OBJ);
        return this;
    }

    public final ColumnBuilder<T> character(int width) {
        config.setWidth(width);
        config.setDataType(DataType.CHAR);
        return this;
    }

    public final ColumnBuilder<T> varchar(int width) {
        config.setWidth(width);
        config.setDataType(DataType.STRING);
        return this;
    }

    public final ColumnBuilder<T> date() {
        config.setFormat(Dates.DATE_PATTERN);
        config.setDataType(DataType.DATE);
        config.setDataPersister(AdaptiveDateStringType.getSingleton());
        return this;
    }

    public final ColumnBuilder<T> datetime() {
        config.setFormat(Dates.DATETIME_PATTERN);
        config.setDataType(DataType.TIME_STAMP);
        config.setDataPersister(AdaptiveDateStringType.getSingleton());
        return this;
    }

    public final ColumnBuilder<T> timestamp() {
        config.setFormat(Dates.TIMESTAMP_PATTERN);
        config.setDataType(DataType.TIME_STAMP);
        config.setDataPersister(AdaptiveDateStringType.getSingleton());
        return this;
    }

    public final ColumnBuilder<T> time() {
        config.setDataType(DataType.STRING);
        return this;
    }

    public final ColumnBuilder<T> byteArray() {
        config.setDataType(DataType.BYTE_ARRAY);
        return this;
    }

    public final <E extends Enume<Integer>> ColumnBuilder<T> enumeInt(Class<E> enumeClass) {
        config.setDataType(DataType.INTEGER);
        config.setDataPersister(new EnumeByIntegerType<>(enumeClass));
        return this;
    }

    public final <E extends Enume<String>> ColumnBuilder<T> enumeString(Class<E> enumeClass, int width) {
        config.setWidth(width);
        config.setDataType(DataType.STRING);
        config.setDataPersister(new EnumeByStringType<>(enumeClass));
        return this;
    }

    public final <E extends Enume<String>> ColumnBuilder<T> enumeChar(Class<E> enumeClass, int width) {
        config.setWidth(width);
        config.setDataType(DataType.CHAR);
        config.setDataPersister(new EnumeByCharType<>(enumeClass));
        return this;
    }

    public final ColumnBuilder<T> unknownEnum(Enum<?> enumVal) {
        config.setUnknownEnumValue(enumVal);
        return this;
    }
        
}
